package com.jason.common.file.word.extension;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.RandomUtil;
import com.jason.common.file.config.StorageConfig;
import com.jason.common.file.word.dto.WordPictureInfo;
import com.jason.common.file.word.util.FileServerUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 默认Word文档中图片文件上传逻辑实现
 *
 * @author gzc
 * @since 2023/10/15
 **/
@Slf4j
@Getter
public class DefaultWordPictureUploadInterfaceImpl implements WordPictureUploadInterface {
    /**
     * 最小触发并发上传图片数量
     */
    private static final int MIN_CONCURRENT_UPDATE_PIC_NUM = 5;
    /**
     * 每个线程上传的图片数量大小
     */
    private int threadGroupPicSize = 5;
    /**
     * 计算并行执行文件上传的线程总数
     */
    private int threadNum;

    private final StorageConfig storageConfig;

    public DefaultWordPictureUploadInterfaceImpl(StorageConfig storageConfig) {
        this.storageConfig = storageConfig;
    }

    public DefaultWordPictureUploadInterfaceImpl(StorageConfig storageConfig, int threadGroupPicSize) {
        this.threadGroupPicSize = threadGroupPicSize;
        this.storageConfig = storageConfig;
    }

    @Override
    public Map<String, String> uploadPic(Map<String, WordPictureInfo> mappingMap) {
        Map<String, String> remoteFileMappingMap = new HashMap<>(16);
        if (CollUtil.isNotEmpty(mappingMap)) {
            // 遍历上传图片
            for (String key : mappingMap.keySet()) {
                WordPictureInfo wordPictureInfo = mappingMap.get(key);
                String tempLocalFilePath = wordPictureInfo.getSourcePictureLocalPath();
                // 根据资源路径获取文件名称
                String prefix = FileNameUtil.getPrefix(tempLocalFilePath);
                // 根据资源路径获取文件类型
                String suffix = FileNameUtil.getSuffix(tempLocalFilePath);
                String newSizePictureFileNameAndType = prefix + StrPool.DASHED + RandomUtil.randomString(8) + StrPool.DOT + suffix;
                String newSizePictureLocalSavePath = FileUtil.getParent(tempLocalFilePath, 1) + File.separator
                        + newSizePictureFileNameAndType;
//                File newSizePictureLocalSaveFile = FileUtil.touch(newSizePictureLocalSavePath);
//                // 按照Word文档中的图片尺寸在同目录下生成新图片
//                ImgUtil.scale(FileUtil.file(tempLocalFilePath), newSizePictureLocalSaveFile, wordPictureInfo.getWidth(), wordPictureInfo.getHeight(), null);
//                // 上传文件
//                String fileUrl = UploadManage.uploadPic(newSizePictureLocalSaveFile, storageConfig);

                // 上传文件
                String fileUrl = "";
                ByteArrayOutputStream outputStream = null;
                try {
                    outputStream = new ByteArrayOutputStream(1024);
                    // 按照Word文档中的图片尺寸在同目录下生成新图片
                    // 注意:对于png图片，由于png支持的不规则图片，透明背景问题，导致压缩后的照片背景变黑
                    // 参考博客:https://blog.csdn.net/xhmico/article/details/122344543
                    resizeImage(FileUtil.getInputStream(tempLocalFilePath), outputStream, wordPictureInfo.getWidth(), wordPictureInfo.getHeight());
                    fileUrl = FileServerUtil.uploadPicture(storageConfig, newSizePictureFileNameAndType, IoUtil.toStream(outputStream));
                } catch (IOException e) {
                    log.error("上传图片发生异常:", e);
                } finally {
                    try {
                        if (outputStream != null) {
                            outputStream.close();
                        }
                    } catch (Exception e) {
                        log.error("关闭文件流发生异常:", e);
                    }
                }
                Assert.notBlank(fileUrl, "图片上传失败，返回文件Url为空-存储平台：{}", storageConfig.getStoragePlatform());
                wordPictureInfo.setNewSizePictureLocalSavePath(newSizePictureLocalSavePath);
                wordPictureInfo.setRemoteSaveUrl(fileUrl);
                remoteFileMappingMap.put(key, fileUrl);
            }
        }
        return remoteFileMappingMap;
    }

    /**
     * 缩放图片
     *
     * @param inputStream  源图片输入流
     * @param outputStream 缩放后的图片输出流
     * @param targetWidth  目标宽度
     * @param targetHeight 目标高度
     * @throws IOException IO操作异常
     */
    private void resizeImage(InputStream inputStream, OutputStream outputStream, int targetWidth, int targetHeight) throws IOException {
        BufferedImage to = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = to.createGraphics();
        // 重要配置Transparency.TRANSLUCENT
        to = g2d.getDeviceConfiguration().createCompatibleImage(targetWidth, targetHeight, Transparency.TRANSLUCENT);
        g2d.dispose();
        g2d = to.createGraphics();
        // 消绘图锯齿：RenderingHints.KEY_ANTIALIASING
//        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 消文字锯齿：RenderingHints.KEY_TEXT_ANTIALIASING
//        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
        Map<RenderingHints.Key, Object> map = new HashMap<>(16);
        map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        map.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
        g2d.setRenderingHints(map);
        g2d.dispose();
        g2d = to.createGraphics();
        BufferedImage bi2 = ImageIO.read(inputStream);
        // 重要配置Image.SCALE_SMOOTH
        Image from = bi2.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);
        g2d.drawImage(from, 0, 0, null);
        g2d.dispose();
        ImageIO.write(to, "png", outputStream);
    }

    @Override
    public Map<String, String> concurrentUploadPic(Map<String, WordPictureInfo> mappingMap,
                                                   ThreadPoolExecutor threadPoolExecutor) {
        Map<String, String> remoteFileMappingMap = new HashMap<>(16);
        if (CollUtil.isNotEmpty(mappingMap)) {
            List<Map<String, WordPictureInfo>> calculateThreadList = this.calculate(mappingMap);
            this.threadNum = calculateThreadList.size();
            if (this.threadNum > 1) {
                List<CompletableFuture<Map<String, String>>> futureList = new ArrayList<>(16);
                for (Map<String, WordPictureInfo> segmentedMappingMap : calculateThreadList) {
                    CompletableFuture<Map<String, String>> future = CompletableFuture.supplyAsync(
                            () -> this.uploadPic(segmentedMappingMap), threadPoolExecutor);
                    futureList.add(future);
                }
                // 并发执行
                List<Map<String, String>> collect = futureList.stream().map(CompletableFuture::join).collect(Collectors.toList());
                for (Map<String, String> segmentedMappingMap : collect) {
                    remoteFileMappingMap.putAll(segmentedMappingMap);
                }
            } else {
                // 主线程执行
                remoteFileMappingMap.putAll(this.uploadPic(mappingMap));
            }
        }
        return remoteFileMappingMap;
    }

    /**
     * 计算线程执行计划列表
     *
     * @param mappingMap 集合
     * @return 线程执行计划列表
     */
    private List<Map<String, WordPictureInfo>> calculate(final Map<String, WordPictureInfo> mappingMap) {
        if (this.threadGroupPicSize < 1) {
            throw new IllegalArgumentException("线程分组图片上传大小不能小于1");
        }
        return this.splitMap(mappingMap, this.threadGroupPicSize);
    }

    /**
     * Map拆分 (指定分组大小)
     *
     * @param map       需要被拆分的集合
     * @param chunkSize 每个分组的大小 (>=1)
     * @return 拆分后的子Map列表
     */
    private List<Map<String, WordPictureInfo>> splitMap(Map<String, WordPictureInfo> map, int chunkSize) {
        // 空map或者分组大小<1，无法拆分
        if (MapUtil.isEmpty(map)) {
            return new ArrayList<>(0);
        }
        // 数量不足50则不分组
        if (map.size() < MIN_CONCURRENT_UPDATE_PIC_NUM) {
            return ListUtil.toList(map);
        }

        // 键值对总数
        int mapSize = map.size();
        // 计算分组个数
        int groupSize = mapSize / chunkSize + (mapSize % chunkSize == 0 ? 0 : 1);
        List<Map<String, WordPictureInfo>> list = new ArrayList<>(groupSize);
        // 子Map列表
        // 只能分1组的情况
        if (chunkSize >= mapSize) {
            list.add(map);
            return list;
        }
        // 每个分组的组内计数
        int count = 0;
        // 子Map
        Map<String, WordPictureInfo> subMap = new HashMap<>(chunkSize);
        for (Map.Entry<String, WordPictureInfo> entry : map.entrySet()) {
            if (count < chunkSize) {
                // 给每个分组放chunkSize个键值对，最后一个分组可能会装不满
                subMap.put(entry.getKey(), entry.getValue());
                // 组内计数+1
                count++;
            } else {
                // 结束上一个分组
                // 当前分组装满了->加入列表
                list.add(subMap);

                // 开始下一个分组
                // 新的分组
                subMap = new HashMap<>(chunkSize);
                // 添加当前键值对
                subMap.put(entry.getKey(), entry.getValue());
                // 组内计数重置为1
                count = 1;
            }
        }
        // 添加最后一个分组
        list.add(subMap);
        return list;
    }

}
